// See LICENSE for license details.
#include <stdint.h>
#include "syscall.h"
#include "file.h"
#include "frontend.h"
#include "mmap.h"
#include <string.h>
#include <errno.h>
#include "proc.h"
#include "sched.h"
#include "fs_file.h"
#include "elf.h"
#include "stat.h"
typedef long (*syscall_t)(long, long, long, long, long, long, long);

#define CLOCK_FREQ 1000000000

extern uint64 sys_dup(uint64 fd);
extern int sys_do_exec(char *path,char **argv);
extern int sys_do_read(int fd,char *buf,size_t n);
extern int sys_do_write(int fd,char *buf,size_t n);
extern int sys_do_fstat(int fd, struct stat_fs* st);
extern int sys_unlink(char *path);
extern int sys_link(char *new,char *old);
extern int sys_chdir(char *path);

void sys_exit(int code)
{
  if(DEBUG_SYSCALL){
    printk("sys exit\n");
  }
  exit(code);
  shutdown(0);
}

ssize_t sys_read(int fd, char* buf, size_t n)
{
  ssize_t r = -EBADF;
  file_t* f = file_get(fd);

  if (f)
  {
    r = file_read(f, buf, n);
    file_decref(f);
  }

  return r;
}

ssize_t sys_pread(int fd, char* buf, size_t n, off_t offset)
{
  ssize_t r = -EBADF;
  file_t* f = file_get(fd);

  if (f)
  {
    r = file_pread(f, buf, n, offset);
    file_decref(f);
  }

  return r;
}

ssize_t sys_write(int fd, const char* buf, size_t n)
{
  ssize_t r = -EBADF;
  file_t* f = file_get(fd);

  if(DEBUG_SYSCALL){
    uint64 pa=va2pa(buf);
    printk("sys_write va %p pa %p n %d\n",buf ,pa,n);
  }
  if(DEBUG_FILE){
       printk("sys_write DEBUG_FILE-----------\n");
       printProcFiles(myproc());
  }
  if (f)
  {
    r = file_write(f, buf, n);
    file_decref(f);
  }

  return r;
}

static int at_kfd(int dirfd)
{
  if (dirfd == AT_FDCWD)
    return AT_FDCWD;
  file_t* dir = file_get(dirfd);
  if (dir == NULL)
    return -1;
  return dir->kfd;
}

int sys_openat(int dirfd, const char* name, int flags, int mode)
{
  int kfd = at_kfd(dirfd);
  if (kfd != -1) {
    file_t* file = file_openat(kfd, name, flags, mode);
    if (IS_ERR_VALUE(file))
      return PTR_ERR(file);

    int fd = file_dup(file);
    if (fd < 0) {
      file_decref(file);
      return -ENOMEM;
    }

    return fd;
  }
  return -EBADF;
}
int sys_open(const char* name, int flags, int mode)
{
  return sys_openat(AT_FDCWD, name, flags, mode);
}
int sys_do_open(const char* name,int omode)
{
  char *path=(char*)va2pa(name);
  return fs_file_open(path,omode);
}
int sys_do_close(int fd)
{
	struct fs_file *f;
	if(fd < 0 || fd >= NOFILE || (f = myproc()->ofsfile[fd]) == 0)
		return -1;
	fs_fileclose(f);
	return 0;
}


int sys_close(int fd)
{
  int ret = fd_proc_close(fd,myproc());
  if (ret < 0)
    return -EBADF;
  return ret;
}


int sys_fstat(int fd, void* st)
{
  int r = -EBADF;
  file_t* f = file_get(fd);

  if (f)
  {
    r = file_stat(f, st);
    file_decref(f);
  }

  return r;
}


ssize_t sys_lseek(int fd, size_t ptr, int dir)
{
  ssize_t r = -EBADF;
  file_t* f = file_get(fd);

  if (f)
  {
    r = file_lseek(f, ptr, dir);
    file_decref(f);
  }

  return r;
}



long sys_fstatat(int dirfd, const char* name, void* st, int flags)
{
  int kfd = at_kfd(dirfd);
  if (kfd != -1) {
    struct frontend_stat buf;
    size_t name_size = strlen(name)+1;
    long ret = frontend_syscall(SYS_fstatat, kfd, va2pa(name), name_size, va2pa(&buf), flags, 0, 0);
    copy_stat(st, &buf);
    return ret;
  }
  return -EBADF;
}

long sys_lstat(const char* name, void* st)
{
  struct frontend_stat buf;
  size_t name_size = strlen(name)+1;
  long ret = frontend_syscall(SYS_lstat, va2pa(name), name_size, va2pa(&buf), 0, 0, 0, 0);
  copy_stat(st, &buf);
  return ret;
}

long sys_stat(const char* name, void* st)
{
  return sys_fstatat(AT_FDCWD, name, st, 0);
}



long sys_linkat(int old_dirfd, const char* old_name, int new_dirfd, const char* new_name, int flags)
{
  int old_kfd = at_kfd(old_dirfd);
  int new_kfd = at_kfd(new_dirfd);
  if (old_kfd != -1 && new_kfd != -1) {
    size_t old_size = strlen(old_name)+1;
    size_t new_size = strlen(new_name)+1;
    return frontend_syscall(SYS_linkat, old_kfd, va2pa(old_name), old_size,
                                        new_kfd, va2pa(new_name), new_size,
                                        flags);
  }
  return -EBADF;
}




int sys_renameat(int old_fd, const char *old_path, int new_fd, const char *new_path) {
  int old_kfd = at_kfd(old_fd);
  int new_kfd = at_kfd(new_fd);
  if(old_kfd != -1 && new_kfd != -1) {
    size_t old_size = strlen(old_path)+1;
    size_t new_size = strlen(new_path)+1;
    return frontend_syscall(SYS_renameat, old_kfd, va2pa(old_path), old_size,
                                           new_kfd, va2pa(new_path), new_size, 0);
  }
  return -EBADF;
}

long sys_mkdirat(int dirfd, const char* name, int mode)
{
  int kfd = at_kfd(dirfd);
  if (kfd != -1) {
    size_t name_size = strlen(name)+1;
    return frontend_syscall(SYS_mkdirat, kfd, va2pa(name), name_size, mode, 0, 0, 0);
  }
  return -EBADF;
}

long sys_mkdir(const char* name, int mode)
{
  return sys_mkdirat(AT_FDCWD, name, mode);
}


long sys_getcwd(const char* buf, size_t size)
{
  return frontend_syscall(SYS_getcwd, va2pa(buf), size, 0, 0, 0, 0, 0);
}

size_t sys_brk(size_t pos)
{

    uint64 addr = myproc()->sz;

    if(pos==0){
       return addr;
    }

    uint64 n=pos-addr;
    if(DEBUG_SYSCALL){
        printk("the pos is %lx    myproc()->sz is %p ,n is %d \n",pos, myproc()->sz ,n);
    }


    if(growproc(n) < 0)
        return -1;


    return pos;
  // return addr;
}

int sys_uname(void* buf)
{
  const int sz = 65;
  strcpy(buf + 0*sz, "Proxy Kernel");
  strcpy(buf + 1*sz, "");
  strcpy(buf + 2*sz, "4.15.0");
  strcpy(buf + 3*sz, "");
  strcpy(buf + 4*sz, "");
  strcpy(buf + 5*sz, "");
  return 0;
}

int sys_fork() {
    return fork();
}

pid_t sys_getpid()
{
   return myproc()->pid;
}


int sys_rt_sigaction(int sig, const void* act, void* oact, size_t sssz)
{
  if (oact)
    memset(oact, 0, sizeof(long) * 3);

  return 0;
}

long sys_time(long* loc)
{
  uintptr_t t = rdcycle() / CLOCK_FREQ;
  if (loc)
    *loc = t;
  return t;
}

int sys_times(long* loc)
{
  uintptr_t t = rdcycle();
  kassert(CLOCK_FREQ % 1000000 == 0);
  loc[0] = t / (CLOCK_FREQ / 1000000);
  loc[1] = 0;
  loc[2] = 0;
  loc[3] = 0;

  return 0;
}



ssize_t sys_writev(int fd, const long* iov, int cnt)
{
  ssize_t ret = 0;
  for (int i = 0; i < cnt; i++)
  {
    ssize_t r = sys_write(fd, (void*)iov[2*i], iov[2*i+1]);
    if (r < 0)
      return r;
    ret += r;
  }
  return ret;
}



static int sys_stub_success()
{
  return 0;
}

static int sys_stub_nosys()
{
  return -ENOSYS;
}


static int
sys_wait(uint64 addr) {
  int res=wait(addr);
  //  printk("s0 %16lx\n",currentproc->context.ra);
   printk("wait syscall ret %d\n",res);
    return res;
}

static int
sys_exec(char *path, char **argv) {

    char * path_pa = (char *) va2pa(path);
    char ** argv_pa= (char **) va2pa(argv);
    for(int argc = 0; argv_pa[argc]; argc++) {
        argv_pa[argc]=(char  *)va2pa( argv_pa[argc]);
    }
    if(DEBUG_EXEC){
      printk(" -----------argv - %p--------argv_pa %p-------8 \n",argv,argv_pa);
      extern pte_t* root_page_table;

    }
    int ret= exec(path_pa,argv_pa);
    if(DEBUG_EXEC)
      printk("exec ret \n",ret);
    return ret;
}

int sys_mknod(const char* name, short major, short minor){
    if(DEBUG_INODE){
      printk("in sys_mknod\n");
    }
    char * path=(char *)va2pa(name);
    if(path==0||major<0||minor<0){
      return -1;
    }
    struct dirent *ip;
    if((ip=create(path,T_DEVICE)) == 0){
      return -1;
    }
    eunlock(ip);
    return 0;
}
int sys_fs_fstat(int fd, void* st){
  st=(void *)va2pa((char *)st);
  if(fd<0||st<0){
    return -1;
  }
  // return filestat(myproc()->ofile[fd], st);
  return 0;
}
long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n)
{
  const static void* syscall_table[] = {
    [SYS_fs_fstat] = sys_fs_fstat,
    [SYS_mknod]= sys_mknod,
    [SYS_exec] = sys_exec,
    [SYS_exit] = sys_exit,
    [SYS_exit_group] = sys_exit,
    [SYS_read] = sys_read,
    [SYS_wait] = sys_wait,
    [SYS_pread] = sys_pread,
    [SYS_write] = sys_write,
    [SYS_openat] = sys_openat,
    [SYS_open] = sys_open,
    [SYS_close] = sys_close,
    [SYS_fstat] = sys_fstat,
    [SYS_lseek] = sys_lseek,
    [SYS_renameat] = sys_renameat,
    [SYS_mkdirat] = sys_mkdirat,
    [SYS_getcwd] = sys_getcwd,
    [SYS_brk] = sys_brk,
    [SYS_uname] = sys_uname,
    [SYS_fork] = sys_fork,
    [SYS_getpid] = sys_getpid,
    [SYS_prlimit64] = sys_stub_nosys,
    [SYS_rt_sigaction] = sys_rt_sigaction,
    [SYS_times] = sys_times,
	[SYS_dup] = sys_dup,
	[SYS_DOEXEC] = sys_do_exec,
	[SYS_DOREAD] = sys_do_read,
	[SYS_DOWRITE] = sys_do_write,
	[SYS_DOCLOSE] = sys_do_close,
    [SYS_DOFSTAT] = sys_do_fstat,
    [SYS_DOOPEN] = sys_do_open,
//	[SYS_link] = sys_link,
//	[SYS_unlink] = sys_unlink,
	[SYS_chdir] = sys_chdir,
    [SYS_writev] = sys_writev,
    [SYS_readlinkat] = sys_stub_nosys,
    [SYS_rt_sigprocmask] = sys_stub_success,
    [SYS_ioctl] = sys_stub_nosys,
    [SYS_getrusage] = sys_stub_nosys,
    [SYS_getrlimit] = sys_stub_nosys,
    [SYS_setrlimit] = sys_stub_nosys,
    [SYS_set_tid_address] = sys_stub_nosys,
    [SYS_set_robust_list] = sys_stub_nosys,
  };

  if(DEBUG_SYSCALL){
      printk("in syscall %d\n",n);
  }


  syscall_t f = 0;

  if (n < ARRAY_SIZE(syscall_table))
    f = syscall_table[n];
  if (!f)
    panic("bad syscall #%ld!",n);

  return f(a0, a1, a2, a3, a4, a5, n);
}
